home *** CD-ROM | disk | FTP | other *** search
/ Amiga Developer CD 2.1 / Amiga Developer CD v2.1.iso / NDK / NDK_2.0 / NDK2.0-1 / HowToDebug < prev    next >
Encoding:
Text File  |  1991-11-21  |  22.3 KB  |  483 lines

  1.  
  2. Debugging Amiga Software
  3.  
  4. by Carolyn Scheppner
  5.  
  6. This article presents some general techniques for debugging software on the 
  7. Amiga.  Before you start programming the Amiga, it's a good idea to read the 
  8. development guidelines in the introductions of the the Addison-Wesley Hardware 
  9. Manual (ISBN 0-201-18157-6) and ROM Kernel Reference Manual: Libraries and 
  10. Devices (ISBN 0-201-18187-8).  These guidelines contain important rules 
  11. which are applicable to all Amiga programs, configurations, and operating 
  12. system releases.  Additional information can be found in the Troubleshooting 
  13. Guide published in Amiga Mail and in the Libraries and Devices manual.  
  14. These documents cover the most common Amiga programming problems.
  15.  
  16. Preventing Bugs
  17.  
  18. The best way to debug software is to prevent bugs in the first place.  
  19. Accordingly, here are seven basic rules you should always follow when 
  20. writing Amiga software:
  21.  
  22. 1. Use Enforcer abd Mungwall while developing your code.
  23.  
  24. 2. Read the latest autodocs and the include file comments for the functions 
  25.   and structures you are using.
  26.  
  27. 3. Always check return values from system functions.  Provide a clean way 
  28.   out and useful messages if something fails!   Assembler programmers -
  29.   remember to TST.L D0 after system calls, before branching on condition codes.
  30.  
  31. 4. C Programmers - Use function prototypes for system functions and your own
  32.   functions.  It's a little extra work but will save you time in the long run 
  33.   by immediately catching most types of improper function calls (missing
  34.   arguments, swapped args, etc.)
  35.  
  36. 5. Keep a version number in your code and update the version number whenever
  37.   changes are made. The 2.0 VERSION command can print out the version
  38.   of any executable which contains a specially formatted version string:
  39.  
  40.    In C:UBYTE *vers="\0$VER: programname 36.10";
  41.    In Asm:vers DC.B 0,'$VER: programname 36.10',0
  42.  
  43.  
  44. 6. Document the code changes for each version.  This can be done manually
  45.    or by using a document control system such as RCS.
  46.  
  47. 7. Test your code!  Test on different configurations, under low memory
  48.    and error conditions, and in conjunction with various watchdog tools.
  49.    Test your product with MungWall, if possible in conjunction with Enforcer
  50.    to catch uses of null pointers and freed memory.
  51.  
  52.  
  53. Finding and Fixing Bugs
  54.  
  55. It is hard to generalize about debugging because different kinds of bugs 
  56. often require very different approaches.  A bug report from a user is quite 
  57. different from a bug that you've just introduced in new code!  However, all 
  58. debugging requires some common steps:
  59.  
  60. 1. Define the problem
  61. 2. Narrow the search and find the bug
  62. 3. Understand, and fix the bug
  63. 4. Make sure you didn't just break something else
  64.  
  65. Steps 3 and 4 are the same for all types of bugs, so we'll cover those last.  
  66. Steps 1 and 2 require different approaches for different kinds of bugs.  
  67. Here are some examples.
  68.  
  69. You've added or written new code and something is broken.
  70.  
  71. 1. Define the problem.  Make sure you can reproduce the problem so you'll 
  72. know when it's gone.  Define as "when I do xxx the program does (or doesn't 
  73. do) do yyy."
  74.  
  75. 2. Narrow the Search.  If you just added a couple of lines of code, and have 
  76. the same development environment as before, check your source code first.  
  77. Check for misuse of existing variables, improper error checking, improper 
  78. use of system or internal functions, and possible changes to conditional 
  79. program flow.
  80.  
  81. If you can't spot the problem, it's time to slow it down and see what's going 
  82. on.  Use a source-level or symbolic debugger, or print/kprint/dprint 
  83. debugging, with delays added if necessary.  One particularly useful type of 
  84. debugging statement is:
  85.  
  86. printf("About to do xxx.  k =%ld Ptr1=$%lx...\n",k,Ptr1); 
  87. Delay(50);
  88.  
  89. The delay gives the debugging line time to be output and gives you a chance 
  90. to read it before the action is taken.  See mydebug.h on the 1991 DevCon 
  91. disks for easy ways to add conditional debugging statements like this to 
  92. your code.
  93.  
  94. By stepping through or printing out your actions and variables, you will 
  95. generally be able to isolate the bug.  If you have isolated the area but 
  96. still can't find the bug, re-read the autodocs for the routines you are 
  97. using.  Check the Troubleshooting Guide in the Addison-Wesley Libraries and 
  98. Devices manual.  Check all other uses of the variables in the problem area.  
  99. If all else fails, isolate the problem code by writing the smallest possible 
  100. example that demonstrates the problem.
  101.  
  102. If the problem is not present in the smallest possible example, then go back 
  103. and check your code.  If the problem is still present, contact CATS for 
  104. assistance or upload the example to BIX (note - one of the quickest ways to 
  105. find bugs in a small source code example is to upload the source to BIX 
  106. amiga.dev/main and ask what's wrong with it).
  107.  
  108.  
  109. B. Your code has intermittent problems that you can't pin down, or appears to 
  110. trash something under certain conditions.
  111.  
  112. 1. Define the problem.  It is difficult to reproduce intermittent problems, 
  113. so try to force the problem to show itself.  First try running your program 
  114. with Enforcer and MungWall.  If you don't have an MMU, 
  115. use WatchMem and MungWall, but be prepared to crash a lot.  If you don't get 
  116. any hits, try the same thing during low-memory situations, heavy multitasking 
  117. and device IO, etc.  If you are doing Exec device IO, try IO_Torture to catch 
  118. premature reuse of IORequests.  Hopefully, you will pick up a hit.
  119.  
  120. 2. Narrow the Search.  If you have no MungWall/Enforcer hits, try some 
  121. debugging statements or source-level debugging to follow the values of your 
  122. variables.  Use TStat to see if your stack usage is high.  Check all possible 
  123. areas where you might be overwriting the end of an array or otherwise 
  124. trashing memory.  Re-read the autodocs for the system functions you are using.
  125.  
  126. If you are reusing an IORequest too soon, check your source code (debugging 
  127. would just slow down your execution and might give the IORequest a chance to 
  128. complete, masking the problem).
  129.  
  130. If you have Enforcer hits, use debugging statements or a debugger to step 
  131. through your code WHILE running MungWall and Enforcer (or WatchMem).
  132. This will allow you to pinpoint where the problem occurs.
  133.  
  134.  
  135.  
  136. C. Your code works fine on one system but not on another.  Or you've received 
  137. a bug report from a user.
  138.  
  139. 1. Define the problem.  First find out the exact configuration of the system 
  140. the problem occurred on.  Important elements include memory configuration and 
  141. addresses, amount of free Chip and Fast RAM, processor type, custom chip 
  142. version, expansion peripherals, OS version, and other software in use when 
  143. the problem occurred.  The Config program on the 1991 DevCon disks is useful 
  144. for printing out much of this information.
  145.  
  146. The memory address ranges can be particularly important now that machines are 
  147. available with memory beyond the 24 bit address limit.  For example, 
  148. overwriting a byte array by one byte now has a good chance of trashing a 
  149. 32-bit address variable, or even your routine's return address on the stack.
  150.  
  151. If a user reports the problem, find out the exact version of your software 
  152. they are running, how they launched the program, and what their stack is set 
  153. to (if launched from CLI).  Try to get them to reproduce the problem in a 
  154. known environment (ie. after booting with a release Workbench diskette).  
  155. Get their phone number and keep it with a record of all of the information 
  156. you can get on the problem.  Keep bug reports in an organized form.  If you 
  157. get two reports on the same problem, you can be pretty sure that the problem 
  158. really exists, and the combined information may help you track it down.
  159.  
  160. 2. Narrow the Search.  Attempt to reproduce the problem.  If you can't 
  161. reproduce it immediately, try stepping through the problem area while using 
  162. Enforcer and MungWall.  If you don't get any hits, try again with less 
  163. memory available and other tasks running.  Try to reproduce the user's 
  164. configuration and environment.  If you still can not reproduce the problem, 
  165. ask the user to come up with a simple repeatable sequence which causes the 
  166. problem on a system booted with a normal release Workbench disk.
  167.  
  168. Read the Troubleshooting guide in the Addison-Wesley Libraries and Devices 
  169. manual or Amiga Mail Technotes for information on the causes for many 
  170. problems that only show up in certain configurations or environments.
  171.  
  172. If all else fails, look carefully at your code for misuse of variables or 
  173. system functions, and for improper error-checking or cleanup after any 
  174. allocation or open.  Check that all cleanups are done in the proper order.
  175.  
  176.  
  177.  
  178.  
  179. D. Your program loses memory.
  180.  
  181. 1. Define the problem.  First make sure that you are actually losing memory.  
  182. Use Flush (from the 1991 DevCon disks) and Avail to check for actual memory 
  183. loss.
  184.  
  185. Set up your system so you have a shell window available and can start your 
  186. program without moving any windows (re-arranging windows causes memory 
  187. fluctuations).  Test for memory loss as follows.  First, try Flush and Avail 
  188. a few times to make sure nothing else in your system is causing memory to 
  189. fluctuate.  Then perform the following steps.
  190.  
  191. 1. FLUSH
  192. 2. AVAIL  (write down the Fast, Chip, and total memory free)
  193. 3. Start your program and use its features
  194. 4. Exit your program
  195. 5. FLUSH
  196. 6. AVAIL  (compare the fast, chip, and total free to previous figures)
  197. 7. If you have a loss, go back to step 2.
  198.  
  199. Testing such as shown above will flush out all properly closed devices,
  200. libraries, and fonts which have been loaded from disk by your program and
  201. other programs.  This allows you to check for actual memory loss.
  202.  
  203. Note that under 2.0, since the audio.device is ROM-resident but
  204. not initialized by the system until it is opened by someone, the
  205. first program to use the audio device or speech capabilities will
  206. appear to cause a small but permanent memory loss.  This is the
  207. memory allocated for the audio device's base structures.  If your program
  208. uses audio or speech, first use the SAY program or SPEECH: before performing
  209. the above memory loss test so that the audio device's initial memory
  210. usage will not interfere with your tests.
  211.  
  212. One special memory loss problems is a continual loss of memory while
  213. a program is running.  This is generally caused by not keeping up
  214. with IntuiMessages, or not freeing Locks.
  215.  
  216.  2. Narrow the search.  Try the above test again, but this time just start 
  217. your program and exit immediately.  If you do not lose memory, try several 
  218. times more, using some of your program's features, and attempt to determine 
  219. which part of your program causes the memory loss.  Check your source code 
  220. for all opens and allocations and check for matching frees and closes, in 
  221. the proper order, for each of them.
  222.  
  223.  
  224.  
  225. The size of a memory loss can also be a clue to the cause.  For example, a 
  226. loss of exactly 24 bytes is probably a Lock() which has not been UnLock()'d.  
  227. Knowing the exact size of the loss (as determined with Flush and Avail) is 
  228. important when you try determine which allocation is not being freed.
  229.  
  230. Some additional tools on the 1991 DevCon disks can help determine where 
  231. memory losses occur. You can use MemMon to record the relative memory usage 
  232. as you test various parts of your program.  Snoop can be used to record all 
  233. memory allocations and frees on a remote terminal, after which SnoopStrip 
  234. can strip out all matching pairs.  MungWall contains an enhanced snoop
  235. option for tracking only the memory allocations of a particular task.
  236. MemList, which outputs the system memory list, can also be useful when
  237. debugging memory loss and fragmentation.
  238.  
  239. The Wedge program, which can restrict its reporting to the function calls 
  240. made by a single task or list of tasks, can also be used to monitor the 
  241. allocations and frees done by your task.  By inserting debugging statements, 
  242. you can mix status messages  ("About to do xxx") with Wedge or MungWall
  243. SNOOP output. Examine the output for an allocation which matches the size
  244. of your loss.  Use LVO's WEDGELINE option to generate command lines
  245. for Wedge.
  246.  
  247.  
  248. Removing Bugs
  249.  
  250. I mentioned steps 3 and 4 earlier.  This is the easy part (finding the bug is 
  251. the hard part).  These steps are the same for most debugging problems. 
  252.  
  253. 3. Understand, and Fix the Bug.  When you find the bug, make sure you 
  254. understand it.  Don't just try something else.  If you are having a problem 
  255. with a system routine, read the autodocs and chapter text for that routine.  
  256. Check the Troubleshooting Guide in the 1.3 Addison- Wesley Libraries and 
  257. Devices manual.  
  258.  
  259. When you understand what is wrong, fix the problem, being especially careful 
  260. not to affect the behavior of any other parts of your program.  Carefully 
  261. document the changes that you make and bump the revision number of the 
  262. program.  Note your changes in the initial comments of the program, and in 
  263. the area where the changes were made.
  264.  
  265.  
  266. 4. Make Sure You Didn't Break Anything New.  Try to reproduce the problem 
  267. several times and make sure it is gone.  Thoroughly test the rest of your 
  268. program and make sure that nothing else has been broken by your fix.  Test 
  269. your program in combination with watchdog tools such as MungWall and Enforcer.
  270.  
  271.  
  272.  
  273.  
  274. Debugging Tools
  275.  
  276. 1. Software Watchdog and Stressing Tools
  277.  
  278. The MMU-based Amiga debugging tool "Enforcer" provides debugging and quality 
  279. assurance capabilities far beyond what was previously possible.  It is now 
  280. possible to find bugs even in code that appears to be working perfectly - 
  281. the kinds of bugs that could cause serious problems on different 
  282. configurations.  Enforcer is able to trap improper low memory accesses, 
  283. writes to ROM, and accesses of non-existent memory - problems which are 
  284. generally caused by use of freed or improperly initialized pointers or 
  285. structures.
  286.  
  287. When used in conjunction with a free memory invalidation tool such
  288. as MungWall, additional illegal memory uses are forced out into
  289. areas trappable by Enforcer.
  290.  
  291. Another extremely useful testing tool, especially for assembler programmers,
  292. is "Scratch" by Bill Hawes.  One of the most surprising compatibility
  293. problems we have seen is improper use or dependence on scratch registers
  294. (D1,A0,A1) after a system call.  Scratch allows you to invalidate the
  295. contents of these scratch registers after system calls so that improper
  296. usage of these registers in your code may be brought out.
  297.  
  298. It is also useful to test your software with stressing tools such
  299. as EatMem, Memoration, and EatCycles, in conjunction with Enforcer
  300. because Enforcer will help to catch use of unsuccessful allocations
  301. immediately.
  302.  
  303. All software should be tested with these tools during development, and 
  304. should be required to pass a test with Enforcer in conjunction with 
  305. MungWall, Scratcher, and IO_Torture before being released and distributed.
  306.  
  307.  
  308. 2. Symbolic and source-level debuggers
  309.  
  310. Symbolic debuggers allow you to trace and single step through your code, and 
  311. examine or change your variables and structures.  The source-level debuggers 
  312. which are provided with some compilers allow you to trace and single step 
  313. your code at the source-level after compiling with special flags.  Debuggers 
  314. can often be used in combination with other tools such as MungWall and
  315. Enforcer to detect exactly where a problem is occurring.
  316.  
  317. 3. Printf() and kprintf() / dprintf() debugging
  318.  
  319. This simple method of debugging allows you to monitor where you are, what 
  320. your variables contain, and anything else you care to print out.  Printf 
  321. debugging is suitable for any process code that is not in a Forbid or 
  322. Disable (printf breaks a Forbid or Disable).  Kprintf (serial) and dprintf 
  323. (parallel) debugging is more flexible and can be used in process, task, or 
  324. interrupt code.  The kprintf function is provided in the debug.lib linker 
  325. library.  The parallel version, dprintf, is provided in the ddebug.lib 
  326. linker library.  See the debug.lib kprintf autodocs for more information on 
  327. the types of formats handled by kprintf and dprintf.
  328.  
  329. Kprintf outputs to the serial port at whatever baud rate the port is 
  330. currently set to.  Generally, kprintf is done at 9600 baud with a terminal, 
  331. or another Amiga running a terminal package, connected to your serial port 
  332. with a null modem serial cable.
  333.  
  334.  
  335.  
  336. However, it is possible to kprintf to yourself (ie. to a terminal package 
  337. running on your own machine) if you have a modem attached to your serial 
  338. port, and your terminal package set to the baud rate of your modem.  
  339. Obviously, if the problem you are debugging causes you to crash, a remote 
  340. terminal is a better choice.  The ASCII capture feature of your terminal 
  341. package can be used to capture the kprintf debugging output for later 
  342. examination.
  343.  
  344. Remote (kprintf/dprintf) debugging is extremely useful when combined with 
  345. other remote debugging tools such as Enforcer and IO_Torture because your own 
  346. debugging statements will be interspersed with the remote output of the 
  347. other debugging tools, allowing you to track what your program is doing 
  348. when problems occur.
  349.  
  350. Printf/kprintf/dprint debugging can be conditionally coded more conveniently 
  351. by using an include file such as mydebug.h (see the DevCon disks).  Mydebug.h 
  352. eliminates the need for messy #ifdef and #endif lines around your debugging 
  353. statements by providing the conditional macros D(bug()), D2(bug()), and 
  354. DQ(bug()) which take printf-style format strings and arguments in their 
  355. inner parens.  One handy feature of these macros is that your debugging 
  356. statements can be be quickly changed from printf's to kprintf's or dprintf's 
  357. by simply setting a flag in mydebug.h and recompiling.
  358.  
  359. Example:  D(bug("I'm here now and a=%ld",a));
  360.  
  361. 4. Other ways to debug low -level code
  362.  
  363. If you can't link with debug.lib, low level code can also be debugged by 
  364. inserting visual or audio cues to let you know where you are.  DebTones.asm 
  365. (in AmigaMail and on the 1991 DevCon disks) demonstrates a small audio tone 
  366. macro suitable for debugging low level code.  Another common method is 
  367. flashing the power LED (see togl_led.asm), or doing an Intuition DisplayBeep() 
  368. to flash the screen.
  369.  
  370. 5. Specialized debugging tools
  371.  
  372. A variety of specialized debugging tools are available for monitoring and 
  373. debugging such things as system function calls, device IO, process status, 
  374. memory usage, and software errors.  These tools can be used without 
  375. recompiling your program and can provide valuable debugging information.  
  376. See the list of tools accompanying this article in the DevCon notes.
  377.  
  378.  
  379.  
  380. How to Use MMU Watchdogs and Other Remote Debugging Tools
  381.  
  382. Remote Serial Debugging
  383.  
  384. Hardware Setup:  When hooking two Amigas together, use a straight RS-232 
  385. cable with a null-modem adaptor, or use a null-modem cable.  When hooking 
  386. an Amiga up to another type of computer or terminal, you may or may not need 
  387. the null-modem (crossed lines) depending on whether the other machine's 
  388. RS-232 port is designed to be basically a sender or a receiver.  Avoid 
  389. connecting lines which are not directly related to RS-232 because different 
  390. computers have various power supplies and grounds on these other lines.  My 
  391. null modem debugging cable is wired as follows:
  392.  
  393. Amiga        Terminal
  394. 1        1
  395. 2        2
  396. 3        3
  397. 7        7
  398. 5, 6, 8        5, 6, 8
  399. 20        20
  400.  
  401. Software:  For remote debugging at 9600 baud, set the sending machine's 
  402. Preferences to 9600 baud, and use a 9600 baud terminal or an Amiga running 
  403. a 9600 baud terminal package (preferably with ASCII capture capability) as 
  404. the receiving machine. Note that other baud rates can also be used for most 
  405. serial debugging because normal serial kprintf's do not modify the serial 
  406. SERPER register and are therefore output at the last baud rate your serial 
  407. hardware was set to.  Test your setup by copying a small text file to SER: 
  408. or try the ktest program from the 1900 DevCon disks.  The output should show 
  409. up on the remote terminal.
  410.  
  411. Applications can output serial debugging statements by using kprintf from C 
  412. or KPrintF from assembler and linking with amiga.lib and debug.lib.  See the 
  413. debug.lib autodocs for more information.  Serial input functions are also 
  414. available.  Also see the mydebug.h conditional debugging macros on the 1991 
  415. DevCon disks.
  416.  
  417. Watchdog software setup:  Make sure your test machine is set to the same 
  418. baud rate as the remote terminal you are connected to.  Turn on the ASCII 
  419. capture of your remote terminal.
  420.  
  421.  
  422. Serial Watchdog software setup:
  423.  
  424. For machines with 68030 or 68020+MMU:
  425. [RUN] MungWall (removable with CTRL-C, or BREAK n if RUN)
  426. [RUN] IO_Torture
  427. Enforcer ON
  428.  
  429. For non-MMU machines    (warning - encourages bad software to crash!)
  430. [RUN] MungWall (removable with CTRL-C, or BREAK n if RUN)
  431. [RUN] IO_Torture
  432. [RUN] WatchMem
  433.  
  434.  
  435. Setup for Local Serial Debugging
  436.  
  437. Hardware:  If you have a modem attached to your serial port, it is possible 
  438. to capture your own serial debugging output locally.  This setup can be 
  439. useful as long as the problem you are debugging is not one which crashes 
  440. the machine.
  441.  
  442. Software:  Run a terminal package at your modem's baud rate to capture the 
  443. kprintfs.  You probably won't be able to test this setup by copying a file 
  444. to SER: (since the terminal package probably has an exclusive open on the 
  445. serial device).  Instead, use a small program like ktest (on the 1991 DevCon 
  446. disks) to test your setup, or, if you already have an MMU watchdog installed, 
  447. try an illegal memory accessor such as Lawbreaker.  Use the terminal package's 
  448. ASCII capture feature to capture your debugging output.
  449.  
  450. Watchdog software setup:  Same as for remote serial debugging, but first 
  451. start up a terminal package on the test machine, at the baud rate of the 
  452. attached modem, with ASCII capture turned on.
  453.  
  454.  
  455. Setup for Parallel Debugging
  456.  
  457. Hardware:  To set up for parallel debugging, attach a parallel printer to 
  458. the Amiga's parallel port and turn the printer on.  Note - if no device is 
  459. attached to the parallel port, parallel debugging statements will hang 
  460. waiting for the port hardware.
  461.  
  462. Software:  Some debugging commands have options for parallel rather than 
  463. serial output.  Examples include Enforcer.par, MungWall.par, IO_Torture.par,
  464. and Wedge with the 'p' option.  Also, your can send your own debugging
  465. statements to the parallel port by using dprintf from C, or DPutFmt from
  466. assembler, and linking with ddebug.lib and amiga.lib.  On the 1991 DevCon
  467. disks, see dtest.asm for an example of calling DPutFmt from assembler, and
  468. mydebug.h for debugging macros which can use printf, kprintf, or dprintf.
  469.  
  470.  
  471. Parallel Watchdog software setup:
  472.  
  473. For machines with 68030 or 68020+MMU:
  474. [RUN] MungWall.par (removable with CTRL-C, or BREAK n if RUN)
  475. [RUN] IO_Torture.par
  476. Enforcer.par ON
  477.  
  478. For non-MMU machines    (warning - encourages bad software to crash!)
  479. [RUN] MungWall.par (removable with CTRL-C, or BREAK n if RUN)
  480. [RUN] IO_Torture.par
  481. [RUN] WatchMem
  482.  
  483.